package com.dymall.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dymall.common.domain.PageDTO;
import com.dymall.common.domain.PageQuery;
import com.dymall.common.exception.BadRequestException;
import com.dymall.common.exception.BizIllegalException;
import com.dymall.common.utils.BeanUtils;
import com.dymall.domain.dto.ItemDTO;
import com.dymall.domain.dto.OrderDetailDTO;
import com.dymall.domain.po.Item;
import com.dymall.mapper.ItemMapper;
import com.dymall.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements ItemService {
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
        return BeanUtil.copyToList(listByIds(ids), ItemDTO.class);
    }

    /**
     * 缓存更新策略 —— 先更新数据库，再删缓存
     * @param items
     */
    @Override
    public void deductStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.dymall.mapper.ItemMapper.updateStock";
        boolean r = false;
        try {
            //对items做批量处理，执行update操作，都执行通过了返回true，有一个执行不通过则返回false
            r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
        } catch (Exception e) {
            throw new BizIllegalException("更新库存异常，可能是库存不足!", e);
        }
        if (!r) {
            throw new BizIllegalException("库存不足！");
        }
        // 删除redis中的缓存
        stringRedisTemplate.delete(List.of("queryByPage","item"));
    }

    @Override
    public PageDTO<ItemDTO> queryItemByPage(PageQuery pageQuery) {
        // 1、从redis中查询分页查询缓存
        String key = "queryByPage";
        String items = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isNotBlank(items)){
            // 2、存在，直接返回
            return JSONUtil.toBean(items, new TypeReference<PageDTO<ItemDTO>>() {},false);
        }
        // 3、不存在，分页查询数据库
        //使用分页查询，将接收到的PageQuery对象(自定义),转化 (将分页查询按 update_time 字段降序排序) 为page对象(MyBatis Plus的分页对象)以使用
        Page<Item> page = page(pageQuery.toMpPage("update_time", false));
        //将Item转化为ItemDTO并用PageDTO<>封装
        PageDTO<ItemDTO> result = PageDTO.of(page, ItemDTO.class);
        // 4、写入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(result), 10, TimeUnit.MINUTES);
        return result;
    }

    @Override
    public ItemDTO queryItemById(Long id) {
        // 1、从redis中查询商品缓存
        String key = "item:" + id;
        String itemDTO = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isNotBlank(itemDTO)){
            // 2、存在，直接返回
            return JSONUtil.toBean(itemDTO, ItemDTO.class);
        }
        // 3、不存在
        // 3.1、检验是否为空值""
        if(itemDTO != null){
            throw new BadRequestException("该商品不存在");
        }
        // 3.2、不为空值，分页查询数据库
        Item item = getById(id);
        if(item == null){
            // 3.3、该商品不存在，将空值写入redis并抛异常
            stringRedisTemplate.opsForValue().set(key, "", 2, TimeUnit.MINUTES);
            throw new BadRequestException("该商品不存在");
        }
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(item));
        ItemDTO result = BeanUtils.copyBean(item, ItemDTO.class);
        // 4、写入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(result), 10, TimeUnit.MINUTES);
        return result;
    }
}
